#include "grid.hpp"

void Grid::initGrid()
{
    data = new short[361 * 361 * 2];

    for(short i = 0; i < 361; i++)
    {
        for(short j = 0; j < 361; j++)
        {
            int m = i * 361 * 2 + j * 2;
            data[m] = i - 180;
            data[m+1] = j - 180;
        }
    }

    element = new uint[360 * 360 * 4];
}

void Grid::initGridGPU()
{
    programID[0] = LoadShaders( "GridVertexShader.vertexshader", "SimpleFragmentShader.fragmentshader" );
    programID[1] = LoadShaders( "GridSphereVertexShader.vertexshader", "SimpleFragmentShader.fragmentshader" );

    MatrixID[0] = glGetUniformLocation(programID[0], "MVP");
    MatrixID[1] = glGetUniformLocation(programID[1], "MVP");

    glGenBuffers(1, &dataBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, dataBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(short) * 361 * 361 * 2, data, GL_STATIC_DRAW);
}

Grid::Grid()
{
    initGrid();
    initGridGPU();

    LOD = 1;

    useProgram = 0;
}

void Grid::createGridLOD()
{
    glDeleteBuffers(1, &elementBuffer);

    int lod = pow(2, LOD);

    int lines = 360 / lod;

    for(short i = 0; i < lines; i++)
    {
        for(short j = 0; j < lines; j++)
        {
            int m = i * lines * 2 + j * 2;
            int k = i * 361 + j;
            element[m] = k*lod;
            element[m+1] = (k + 1)*lod;
        }
    }

    int last = lines * lines * 2 + 1;

    for(short i = 0; i < lines; i++)
    {
        for(short j = 0; j < lines; j++)
        {
            int m = last + i * lines * 2 + j * 2;
            int k = (i + j * 361);
            element[m] = k*lod;
            element[m+1] = (k+1)*lod;
        }
    }

    elementBufferSize = lines*lines * 4;

    glGenBuffers(1, &elementBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * elementBufferSize, element , GL_STATIC_DRAW);
}

void Grid::getGridLOD(float cameraHeight)
{
    for(int i = 0; i < 3; i++)
    {
        if(cameraHeight > (i*1000) && cameraHeight < (i*1000 + 1000) && LOD != i)
        {
            LOD = i;
            createGridLOD();
            break;
        }
    }
    if(cameraHeight > 4000 && LOD != 3 )
    {
        LOD = 3;
        createGridLOD();
    }
}

void Grid::drawGrid(mat4 VP, float cameraHeight)
{
    MVP = VP;

    getGridLOD(cameraHeight);

    glUseProgram(programID[useProgram]);

    glUniformMatrix4fv(MatrixID[useProgram], 1, GL_FALSE, &MVP[0][0]);

    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, dataBuffer);
    glVertexAttribPointer(
        0,                  // attribute. No particular reason for 0, but must match the layout in the shader.
        2,                  // size
        GL_SHORT,           // type
        GL_FALSE,           // normalized?
        0,                  // stride
        (void*)0            // array buffer offset
    );

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer);

    glDrawElements(
        GL_LINES,     // mode
        elementBufferSize ,   // count
        GL_UNSIGNED_INT,   // type
        (void*)0          // element array buffer offset
    );

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
}

void Grid::setView(short _view)
{
    useProgram = _view;
    createGridLOD();
}
